//boost::asio 服务端同步方式通信 - 每个链接使用一个线程处理
//原文链接：https://llfc.club/category?catid=225RaiVNI8pFDD5L4m807g7ZwmF#!aid/2Nr3U2vU2UxYM0odYRGAk3wWO1S

#include <boost/asio/io_context.hpp>
#include <cstdint>
#include <iostream>
#include <string.h>
#include <utility>
#include <memory>
#include <set>
#include <thread>
#include <boost/asio.hpp>

#include "../log.h"
using socket_ptr = std::shared_ptr<boost::asio::ip::tcp::socket>;
std::set<std::shared_ptr<std::thread>> thread_set;

//处理客户端连接
void session(socket_ptr sock)
{
    using namespace boost::asio::ip;
    constexpr uint32_t MAX_LEN = 1024;
    try
    {   
        while(true)
        {
            std::unique_ptr<char[]> buf(new char[MAX_LEN]);
            memset(buf.get(), 0, 1024);
            boost::system::error_code error;
            size_t len = sock->read_some(boost::asio::buffer(buf.get(), MAX_LEN), error);
            if(error == boost::asio::error::eof)
            {
                log_warning("Client closed connection ip:{}, port:{}", sock->remote_endpoint().address().to_string(), sock->remote_endpoint().port());
                break; // Connection closed cleanly by peer.
            }
            else if(error)
            {
                throw boost::system::system_error(error);
            }

            log_info("ip:{}, port:{}, recv:{}", sock->remote_endpoint().address().to_string(), sock->remote_endpoint().port(), buf.get());
            sock->write_some(boost::asio::buffer(buf.get(), len));
        }
    }
    catch(const std::exception& e)
    {
        log_error("session exception:{}", e.what());
    }
}


//接受客户端连接
void accept(boost::asio::io_context& io_ctx, uint16_t port)
{
    using namespace boost::asio::ip;
    tcp::acceptor acceptor(io_ctx, tcp::endpoint(tcp::v4(), port));
    log_debug("local ip;{}, local port:{}", acceptor.local_endpoint().address().to_string(), acceptor.local_endpoint().port());
    while(true)
    {
        socket_ptr sock(new tcp::socket(io_ctx));
        acceptor.accept(*sock);
        auto t = std::make_shared<std::thread>(session, sock);
        thread_set.insert(t); //防止线程局部变量t被销毁
    }
}

int main(void)
{
    using namespace boost::asio::ip;

    boost::asio::io_context io_ctx;

    try
    {
        accept(io_ctx, 8080);
        // io_ctx.run();
        for(auto& t : thread_set)
        {
            t->join();
        }
    }
    catch (const std::exception& e)
    {
        log_error("Exception: {}", e.what());
    }

    return 0;
}